package com.jimo.app.util;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.content.FileProvider;

import com.android.tools.smali.dexlib2.AccessFlags;
import com.android.tools.smali.dexlib2.DexFileFactory;
import com.android.tools.smali.dexlib2.Opcode;
import com.android.tools.smali.dexlib2.Opcodes;
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation;
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c;
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedClassDef;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile;
import com.android.tools.smali.dexlib2.dexbacked.DexBackedMethod;
import com.android.tools.smali.dexlib2.dexbacked.raw.HeaderItem;
import com.android.tools.smali.dexlib2.iface.ClassDef;
import com.android.tools.smali.dexlib2.iface.DexFile;
import com.android.tools.smali.dexlib2.iface.Field;
import com.android.tools.smali.dexlib2.iface.Method;
import com.android.tools.smali.dexlib2.iface.MethodImplementation;
import com.android.tools.smali.dexlib2.iface.MethodParameter;
import com.android.tools.smali.dexlib2.iface.MultiDexContainer;
import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef;
import com.android.tools.smali.dexlib2.immutable.ImmutableField;
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod;
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference;
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference;
import com.android.tools.smali.dexlib2.rewriter.DexFileRewriter;
import com.android.tools.smali.dexlib2.rewriter.DexRewriter;
import com.android.tools.smali.dexlib2.rewriter.Rewriter;
import com.android.tools.smali.dexlib2.rewriter.RewriterModule;
import com.android.tools.smali.dexlib2.rewriter.Rewriters;
import com.android.tools.smali.dexlib2.writer.DexWriter;
import com.android.tools.smali.dexlib2.writer.builder.DexBuilder;
import com.android.tools.smali.dexlib2.writer.io.FileDataStore;
import com.android.tools.smali.dexlib2.writer.pool.DexPool;
import com.google.common.collect.ImmutableList;

import org.dom4j.Element;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import in.sunilpaulmathew.sCommon.FileUtils.sFileUtils;

public class Util {
    public static String TAG="emmmmmmmmm";


    public  static boolean isAppInstalled(Context context, String packageName) {
        try {
            context.getPackageManager().getPackageInfo(packageName, 0);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }
    public static void uninstallApp( Context context,String packageName) {
        Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, Uri.parse("package:" + packageName));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context. startActivity(intent);
    }
    public static void installApk(Context context) {
        File apkFile = new File(context.getExternalCacheDir(), "origin_sign.apk");
        if (!apkFile.exists()) {
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!context.getPackageManager().canRequestPackageInstalls()) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
                        Uri.parse("package:" +context. getPackageName()));
                context. startActivity(intent);
                return;
            }
        }

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Uri uri;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            uri = FileProvider.getUriForFile(context,
                    context.getPackageName() + ".fileprovider", apkFile);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            uri = Uri.fromFile(apkFile);
        }

        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        context. startActivity(intent);
    }
    public static void signApks(File apk, File signedAPK, Context context) {
        try {
            checkAndPrepareSigningEnvironment(context);

            APKSigner apkSigner = new APKSigner(context);
            apkSigner.sign(apk, signedAPK);
        } catch (Exception ignored) {}
        apk.delete();
    }
    public static void checkAndPrepareSigningEnvironment(Context context) {
        if (APKSigner.getPK8PrivateKey(context).exists()) {
            return;
        }

        sFileUtils.mkdir(new File(context.getFilesDir(), "signing"));
        sFileUtils.copyAssetFile("APKEditor.pk8", APKSigner.getPK8PrivateKey(context), context);
    }
    static int widthOf(String typeDesc) {
        // long(J) / double(D) 占 2 个寄存器，其它都是 1
        char c = typeDesc.charAt(0);
        return (c == 'J' || c == 'D') ? 2 : 1;
    }

    static int getThisRegisterIndex(Method method) {
        int total = method.getImplementation().getRegisterCount(); // .registers 总数
        int paramRegs = 0;
        for (MethodParameter p : method.getParameters()) {
            paramRegs += widthOf(p.getType());
        }
        // 非 static 方法 +1（this）
        if (!AccessFlags.STATIC.isSet(method.getAccessFlags())) {
            paramRegs += 1;
        }
        return total - paramRegs; // 这里就是 p0 的实际寄存器号
    }
    @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
    public static String getDex(String filePath, Element appElement, String dexPath,int functType) throws IOException {



        String launcherActivityName=null;
        for (Element activity : appElement.elements("activity")) {
            for (Element intentFilter : activity.elements("intent-filter")) {
                Element action = intentFilter.element("action");
                Element category = intentFilter.element("category");
                if(action!=null&&category!=null){
                    String actionValue = action.attributeValue("name");
                    String categoryValue = category.attributeValue("name");
                    if(actionValue.equals("android.intent.action.MAIN")&&categoryValue.equals("android.intent.category.LAUNCHER")){
                        launcherActivityName=activity.attributeValue("name");
                        break;
                    }
                }
            }
        }


        String launcherName="L"+launcherActivityName.replaceAll("\\.","/")+";";
        String modifyDexName=null;
        DexPool dexPool = new DexPool(Opcodes.getDefault());
        MultiDexContainer<? extends DexBackedDexFile> apk = DexFileFactory.loadDexContainer(new File(filePath), Opcodes.getDefault());
        for (String dexEntryName : apk.getDexEntryNames()) {
            DexBackedDexFile dex = Objects.requireNonNull(apk.getEntry(dexEntryName)).getDexFile();
            int classCount = dex.getClassSection().size();
            int classStartOffset = dex.getBuffer().readSmallUint(HeaderItem.CLASS_START_OFFSET);
            boolean found = false;
            int matchingClassIndex = -1;
            for (int i = 0; i < classCount; i++) {
                int classDefOffset = classStartOffset + i * 32; // 每个 class_def 32 字节
                int classIdx = dex.getBuffer().readSmallUint(classDefOffset); // class_idx (4 字节)
                String classType = dex.getTypeSection().get(classIdx); // 获取类型字符串
                if (classType.equals(launcherName)) {
                    found = true;
                    matchingClassIndex = i;
                    break;
                }
            }
            if (!found) {
                System.out.println("Target class not found in " + dexEntryName);
                continue;
            }
            modifyDexName=dexEntryName;
            if(functType==0){
                DexFileRewriter fileRewriter = getDexFileRewriter(launcherName);
                DexFile newDex = fileRewriter.rewrite(dex);
                DexFileFactory.writeDexFile(dexPath+"/"+dexEntryName,newDex);
            }else{
                DexBackedClassDef classDef = dex.getClassSection().get(matchingClassIndex);
                for (DexBackedClassDef aClass : dex.getClasses()) {
                    if (aClass.getType().equals(launcherName) && aClass.getSize() == classDef.getSize()) {
                        List<Method> modifiedMethods = new ArrayList<>();
                        for (DexBackedMethod method : aClass.getMethods()) {
                            if (method.getName().equals("onCreate")
                                    && method.getParameters().size() == 1
                                    && method.getParameters().get(0).getType().equals("Landroid/os/Bundle;")) {
                                MutableMethodImplementation newImpl = new MutableMethodImplementation(method.getImplementation());

                                newImpl.addInstruction(0, new BuilderInstruction35c(
                                        Opcode.INVOKE_STATIC,        // 调用静态方法
                                        1,                           // 1 个参数（this）
                                        getThisRegisterIndex(method),
                                        0,
                                        0,
                                        0,
                                        0,              // 寄存器 p0 提供 this，未使用的寄存器填 0
                                        new ImmutableMethodReference(
                                                "Lcom/bytecat/algui/MemApp;", // 类名（Dalvik 格式）
                                                "init",                       // 方法名
                                                List.of("Landroid/app/Activity;"), // 参数类型：Activity
                                                "V"                           // 返回值：void
                                        )
                                ));
                                modifiedMethods.add(new ImmutableMethod(
                                        method.getDefiningClass(),
                                        method.getName(),
                                        method.getParameters(),
                                        method.getReturnType(),
                                        method.getAccessFlags(),
                                        method.getAnnotations(),
                                        method.getHiddenApiRestrictions(),
                                        newImpl
                                ));
                            } else {
                                modifiedMethods.add(ImmutableMethod.of(method));
                            }
                        }
                        List<ImmutableField> immutableFields = new ArrayList<>();
                        for (Field f : aClass.getFields()) {
                            immutableFields.add(ImmutableField.of(f));
                        }
                        dexPool.internClass(new ImmutableClassDef(
                                aClass.getType(),
                                aClass.getAccessFlags(),
                                aClass.getSuperclass(),
                                aClass.getInterfaces(),
                                aClass.getSourceFile(),
                                aClass.getAnnotations(),
                                immutableFields,
                                modifiedMethods
                        ));
                    } else {
                        dexPool.internClass(ImmutableClassDef.of(aClass));
                    }
                }
                dexPool.writeTo(new FileDataStore(new File(dexPath,dexEntryName)));
            }
            break;
            }
        return modifyDexName;
    }

    @NonNull
    private static DexFileRewriter getDexFileRewriter(String launcherName) {
        DexRewriter rewriter = new DexRewriter(new RewriterModule() {
            @Override
            public Rewriter<ClassDef> getClassDefRewriter(Rewriters rewriters) {
                return classDef -> {
                    if (classDef.getType().equals(launcherName)) {
                        List<Method> modifiedMethods = new ArrayList<>();
                        for (Method method : classDef.getMethods()) {
                            if (method.getName().equals("onCreate") && method.getParameters().size() == 1 && method.getParameters().get(0).getType().equals("Landroid/os/Bundle;")) {
                                MethodImplementation oldImpl = method.getImplementation();
                                if (oldImpl != null) {
                                    MutableMethodImplementation newImpl = new MutableMethodImplementation(oldImpl);
                                    newImpl.addInstruction(0, new BuilderInstruction35c(
                                            Opcode.INVOKE_STATIC,        // 调用静态方法
                                            1,                           // 1 个参数（this）
                                            getThisRegisterIndex(method), 0, 0, 0, 0,              // 寄存器 p0 提供 this，未使用的寄存器填 0
                                            new ImmutableMethodReference(
                                                    "Lcom/bytecat/algui/MemApp;", // 类名（Dalvik 格式）
                                                    "init",                       // 方法名
                                                    List.of("Landroid/app/Activity;"), // 参数类型：Activity
                                                    "V"                           // 返回值：void
                                            )
                                    ));
                                    modifiedMethods.add(new ImmutableMethod(method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), null, newImpl));
                                } else {
                                    modifiedMethods.add(method);
                                }
                            } else {
                                modifiedMethods.add(method);
                            }
                        }
                        return new ImmutableClassDef(classDef.getType(), classDef.getAccessFlags(), classDef.getSuperclass(),
                                classDef.getInterfaces(), classDef.getSourceFile(), classDef.getAnnotations(), classDef.getFields(), modifiedMethods);
                    }
                    return classDef;
                };
            }
        });
        DexFileRewriter fileRewriter = new DexFileRewriter(rewriter);
        return fileRewriter;
    }

    @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
    public static Map<String, byte[]> getSo(Context context, Set<String> abis) {
        Map<String, byte[]> sos = new HashMap<>();
        List<String> allAbis = Arrays.asList("arm64-v8a", "armeabi-v7a", "x86", "x86_64");
        Set<String> targets = (abis == null || abis.isEmpty()) ? new HashSet<>(allAbis) : abis;
        for (String abi : targets) {
            if (allAbis.contains(abi)) {
                try (InputStream is = context.getAssets().open("lib/" + abi + "/libAlgui.so")) {
                    byte[] bytes = is.readAllBytes();
                    sos.put("lib/" + abi + "/libAlgui.so", bytes);
                } catch (IOException e) {
                    // 找不到某个架构的 so，跳过
                }
            }
        }

        return sos;
    }
    public  static void addDynamicPermission(Element root, String permission) {
        List<Element> permissions = root.elements("permission");
        boolean permission_exists = permissions.stream().anyMatch(e -> permission.equals(e.attributeValue("android:name")));
        if (!permission_exists) {
            Element newPerm = root.addElement("permission");
            newPerm.addAttribute("android:name", permission);
            newPerm.addAttribute("android:protectionLevel", "0x00000000");
        }else {
            Log.i(TAG, "已有动态权限");
        }
    }

    public  static void addUserPermission(Element root, String permission) {
        List<Element> permissions = root.elements("uses-permission");
        boolean permission_exists = permissions.stream().anyMatch(e -> permission.equals(e.attributeValue("android:name")));
        if (!permission_exists) {
            Log.i(TAG, "正在添加动态权限");
            Element newPerm = root.addElement("uses-permission");
            newPerm.addAttribute("android:name", permission);
        }else {
            Log.i(TAG, "已有动态权限");
        }
    }

    public static void addService(Element appElement,
                                   String serviceName,
                                   String servicePermission,
                                   String enabled,
                                   String process,
                                   String exported,
                                   String filterActionName,
                                   boolean hasFilterActionCategory
    ) {
        List<Element> services = appElement.elements("service");
        boolean service_exists = services.stream().anyMatch(s -> serviceName.equals(s.attributeValue("android:name")));
        if (!service_exists) {
            Log.i(TAG, "开始添加命令服务");
            Element service = appElement.addElement("service");
            service.addAttribute("android:name", serviceName);
            if(enabled!=null){
                service.addAttribute("android:enabled", enabled);
            }
            if(exported!=null){
                service.addAttribute("android:exported", exported);
            }
            if(process!=null){
                service.addAttribute("android:process", process);
            }
            if(servicePermission!=null){
                service.addAttribute("android:permission", servicePermission);
            }
            if(filterActionName!=null){
                Element intentFilter = service.addElement("intent-filter");
                Element action = intentFilter.addElement("action");
                action.addAttribute("android:name", filterActionName);
                if(hasFilterActionCategory){
                    Element category = intentFilter.addElement("category");
                    category.addAttribute("android:name", "android.intent.category.DEFAULT");
                }
            }
        } else {
            Log.i(TAG, "命令服务已存在，不重复添加");
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
    public static byte[] getAssetsDex(Context context) throws IOException {
        byte[] bytes;
        InputStream dexIs = context.getAssets().open("injector.dex");
        bytes = dexIs.readAllBytes();
        dexIs.close();
        return bytes;
    }
}
